home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d1 / adjram31.arc / ADJRAM31.C < prev    next >
C/C++ Source or Header  |  1986-10-02  |  36KB  |  1,128 lines

  1. /*      Adjustable Ram Disk
  2.  
  3.  (c)    Copyright 1986 by Gary Cramblitt.  All Rights Reserved.
  4.  
  5.  v2.2    1 Jul 86   Initial version
  6.  v2.3   24 Aug 86   Bug.  FAT media byte not updated properly.
  7.  v2.4    29 Aug 86   If current drive is memory disk drive, reset current
  8.             directory on exit.
  9.  v2.5    30 Aug 86   Increase FAT to permit max size of 2043K;
  10.             Increase size of root directory to 128 entries;
  11.             Start code for /E option (Expanded Memory Support)
  12.  v3.0    30 Aug 86   Finish code for /E option.
  13.  v3.1    2 Oct 86   Fix shrink bug.  Not packing subdirectories properly
  14.  
  15.     For program usage, see the last routine.
  16.  
  17.     This program is coded in DeSmet C v2.4.  Any function beginning
  18. with underscore (_) is a non-standard routine from the DeSmet library.
  19. They are:
  20.     _showcs()
  21.         Synopsis: unsigned _showcs()
  22.  
  23.         Returns the current value of CS.
  24.  
  25.     _showds()
  26.         Synopsis: unsigned _showds()
  27.  
  28.         Returns the current value of DS (= SS in DeSmet C).
  29.  
  30.     _peek()
  31.         Synopsis: char _peek(offset,segment)
  32.               char *offset;
  33.               unsigned segment;
  34.         Returns the byte at specified far address.
  35.  
  36.     _doint()
  37.         Synopsis: set any or all of the externs
  38.                   _rax,_rbx,_rcx,_rdx,_rsi,_rdi,_res,_rds
  39.         followed by
  40.               _doint(interrupt number);
  41.         Performs the specified interrupt with the specified
  42.         registers set from the externs.
  43.  
  44.         After the call, _rax, etc. can be used.  _carryf
  45.         and _zerof are extern char variables set to 1 if
  46.         the carry or zero flag is set.
  47.  
  48. In addition, the following routines are semi_standard, and may have
  49. slightly different implementations with your compiler:
  50.  
  51.     strncmp()
  52.         Synopsis: char *strncmp(leftstring,rightstring,max)
  53.                   char *leftstring, *rightstring
  54.               int max        
  55.         strncmp() compares up to a specified number of chars
  56.         in two strings.  It returns 0 if the specified number
  57.         of characters in the string are the same.
  58.  
  59.         free()
  60.         Synopsis: int free(pointer)
  61.               char *pointer
  62.         free memory previously allocated (by calloc).  Called
  63.         "cfree" in K&R.
  64. */
  65.  
  66. /* ==== Definitions ==== */
  67.  
  68. /* ---- Overall Definitions ---- */
  69.  
  70. #define true            1
  71. #define false            0
  72. #define max_size        2043    /* max memory disk size    */
  73.  
  74. /*
  75.    ---- To compile without LOTUS/INTEL/Microsoft Expanded Memory
  76.     support, change the "1" to "0" in the next statement.  This
  77.     will save about 2500 bytes in the .EXE file.
  78. */
  79.  
  80. #define em_support        1    /* compile for EM support */
  81.  
  82. /*
  83.     The following symbols must correspond to the same symbols in
  84.     file amdisk.asm.  If one is changed, so must the other.
  85.     This is because the first memory block may not be
  86.     deallocated.
  87. */
  88.  
  89. #define    min_size        64    /* minimum 64K = 128 sectors */
  90. #define    sec_per_blk        64    /* increment in 32K blocks */
  91.  
  92. /*
  93.    ---- Disk definitions.  Note: These constants should be made into
  94.     variables or functions if this program's algorithms need to
  95.     be generalized to disks of any type, especially high density disks.
  96.     Since this program works only in conjunction with amdisk.asm,
  97.     is OK to make them constants here.
  98. */
  99.  
  100. #define bytes_per_sec        512    /* 512 bytes per sector */
  101. #define par_per_sec        (512/16)
  102.                     /* 32 paragraphs per sector */
  103. #define sec_per_K        (1024/512)
  104.                     /* sectors per 1024 bytes */
  105. #define dir_per_sec        (512/sizeof(struct dir_entry))
  106.                     /* directory entries per sector */
  107. #define em_pag_per_blk        2    /* two 16K EM pages per mem block */
  108.  
  109. /* ---- Program Segment Prefix ---- */
  110.  
  111. #define    environment_segment    0x2c    /* segment address of the environment */
  112.  
  113. /* ---- DOS interrupts ---- */
  114.  
  115. #define    dosi_dosf        0x21    /* DOS function interrupt */
  116. #define    dosi_dsk_read        0x25    /* DOS absolute disk read interrupt */
  117. #define    dosi_dsk_write        0x26    /* DOS absolute disk write interrupt */
  118.  
  119. /* ---- User interrupts ---- */
  120.  
  121. #define usri_emm        0x67    /* LOTUS/INTEL/Microsoft Expanded
  122.                        Memory Manager */
  123. /* ---- DOS functions ---- */
  124.  
  125. #define dosf_seldisk        0x0e    /* set default disk */
  126. #define dosf_getdisk        0x19    /* get current default disk */
  127. #define dosf_getver        0x30    /* get DOS version number */
  128. #define    dosf_keepprc        0x31    /* keep process (term and stay resident) */
  129. #define dosf_drvfre        0x36    /* get disk free space */
  130. #define dosf_chdir        0x3b    /* set default directory */
  131. #define dosf_openh        0x3d    /* open file handle */
  132. #define dosf_closeh        0x3e    /* close file handle */
  133. #define dosf_ioctl        0x44    /* IOCTL */
  134. #define dosf_cwd        0x47    /* get current directory */
  135. #define dosf_alloc        0x48    /* allocate memory block */
  136. #define    dosf_dealloc        0x49    /* deallocate memory block */
  137. #define    dosf_setblk        0x4a    /* modify memory block */
  138.  
  139. /* ---- LOTUS/INTEL/Microsoft Expanded Memory Manager functions ---- */
  140.  
  141. #define emm_status        0x40    /* get manager status */
  142. #define emm_get_PFseg        0x41    /* get page frame segment */
  143. #define emm_get_pages        0x42    /* get number of pages */
  144. #define emm_get_handle        0x43    /* get handle and allocate memory */
  145. #define emm_map_memory        0x44    /* map memory */
  146. #define emm_fre_handle        0x45    /* free handle and memory */
  147. #define emm_get_ver        0x46    /* get EMM version */
  148. #define emm_sav_map        0x47    /* save mapping context */
  149. #define emm_res_map        0x48    /* restore mapping context */
  150. #define emm_num_handles        0x4b    /* get number of EMM handles */
  151. #define emm_hdl_pages        0x4c    /* get pages owned by handle */
  152. #define emm_all_pages        0x4d    /* get pages for all handles */
  153. #define emm_pag_map        0x4e    /* get or set page map */
  154.  
  155. #define nor_flg            0    /* memory block is in normal memory */
  156. #define em_flg            1    /* memory block is in expaned memory */
  157.  
  158. /*
  159.    ---- DOS Errors ----
  160.     These codes are returned by program and can be tested with DOS
  161.     IF statement.
  162. */
  163.  
  164. #define    dose_noerr        0    /* no error */
  165. #define dose_invfunc        1    /* invalid function */
  166. #define dose_arena        7    /* arena trashed */
  167. #define dose_noram        8    /* not enough memory */
  168. #define dose_invdrv        15    /* invalid drive */
  169.  
  170. /* ---- Directory definitions ---- */
  171.  
  172. #define never_used        0    /* directory entry never used */
  173. #define erased            0xe5    /* file has been erased */
  174. #define dir_bit            0x10    /* this bit indicates subdirectory */
  175.  
  176. /* ---- FAT definitions ---- */
  177.  
  178. #define available        0    /* cluster is available for use */
  179. #define bad            0xff7    /* cluster is bad */
  180. #define last_low        0xff8    /* last cluster for the file */
  181. #define last_high        0xfff    /* last cluster for the file */
  182.  
  183. extern unsigned _rax, _rbx, _rcx, _rdx, _rsi, _rdi, _res, _rds;
  184. extern char    _carryf, _zerof;
  185.  
  186. /* ---- Directory entry ---- */
  187.  
  188. struct dir_entry {
  189.   union dir_name {
  190.     char        name[8];
  191.     unsigned char    status;
  192.   } u_name;
  193.   char        ext[3];
  194.   unsigned char    attr;
  195.   unsigned char    reserved[10];
  196.   unsigned int    time;
  197.   unsigned int    date;
  198.   unsigned int    first_cluster;
  199.   unsigned long    size;
  200. };
  201.  
  202. /* ==== Global Data Storage ==== */
  203.  
  204. unsigned int pgm_seg;        /* CS at start of program saved here */
  205. int    drive_number;        /* memory disk drive number A=0, B=1, etc */
  206. int    mdisk_size;        /* user's desired memory disk size in K */
  207. int    mdisk_secs;        /* user's desired memory disk size in sectors */
  208. int    mdisk_chg;        /* the difference between disk's current size
  209.                    and the desired size (in sectors) */
  210. int    free_secs;        /* unused sectors in the memory disk */
  211. int    first_dir_sector;
  212. int    last_dir_sector;      /* loc of directory */
  213. int    first_fat_sector;
  214. int    last_fat_sector;    /* loc of FAT */
  215. int    free_cluster;        /* ptr to first free cluster */
  216. int    first_data_sector;    /* first sector after the last FAT */
  217.  
  218. /* ---- Memory Disk Boot Record ---- */
  219.  
  220. struct    boot_record {
  221.   unsigned char jmp[3];      /* non-bootable (no jump instruction ) */
  222.   char      ident[8];      /* identification */
  223.   unsigned int  bytes_in_sector;/* bytes/sector */
  224.   unsigned char sec_per_cluster;/* sectors/cluster */
  225.   unsigned int  bpb_reserved;      /* reserved sectors */
  226.   unsigned char bpb_fats;      /* number of FAT's */
  227.   unsigned int  bpb_root;      /* directory entries in root */
  228.   unsigned int  bpb_total;      /* total number of sectors */
  229.   unsigned char bpb_media;      /* media byte = number of mem blocks */
  230.   unsigned int  bpb_fat_size;      /* sectors/FAT */
  231.  
  232.   unsigned int  sec_per_track;    /* sectors/track */
  233.   unsigned int  heads;          /* number of heads */
  234.   unsigned int  hidden;          /* hidden sectors */
  235.  
  236.   struct mem_blk_table_entry {
  237.     unsigned char typ;          /* type of blk: 0 = normal 1 = EM */
  238.     unsigned int par;          /* paragraph address of block */
  239.     unsigned int siz;          /* number of sectors in the memory block */
  240.     unsigned int hdl;          /* expanded memory handle */
  241.   } mem_blk_table[max_size/sec_per_blk];
  242.   char    rest_of_record[bytes_per_sec - sizeof(struct boot_record)];
  243. } boot_sec;
  244.  
  245. /* ---- Memory disk default pathname ----- */
  246.  
  247.   struct pathname {
  248.     char drv;
  249.     char colon;
  250.     char slash;
  251.     char dir[64];
  252.   } mdisk_pathname; /* Ram disk's current pathname */
  253.  
  254. /* ---- Default drive ---- */
  255.  
  256.   unsigned char default_drive;
  257.  
  258. /* ---- Expanded Memory Manager ----- */
  259.  
  260.   unsigned int em_PFseg;    /* EMM's page frame segment */
  261.   int em_requested;        /* true if to expand using EM */
  262.   char em_device_name[] = "EMMXXXX0"; /* EMM device name */
  263.  
  264. /* ---- File Allocation Table ----*/
  265.  
  266.   unsigned char *fat;
  267.  
  268. /* ---- Declare types of library functions which return non-integer values */
  269.  
  270. char *calloc();        /* allocates memory for given number of things */
  271. char *strncmp();        /* comapares 2 strings of specified length */
  272. unsigned _showcs();    /* returns current CS register */
  273. char _peek();        /* returns a byte given segment and offset */
  274.  
  275. /* ==== MAIN ROUTINE ==== */
  276.  
  277. main(argc, argv)
  278.   int argc;
  279.   char *argv[];
  280.  
  281. {
  282.   int    err_code;        /* exit error code ( 0 if no error ) */
  283.   int    result;
  284.   int    j;
  285.   int    k;
  286.   int    m;
  287.  
  288.   /*
  289.      ---- Begin by saving the CS, which points to just after the 256-byte
  290.       PSP.  By subtracting 16, we get the segment pointer for the
  291.       program segment. This is, of course, highly DeSmet C dependent
  292.       code.  Hopefully, your compiler has some mechanism for getting
  293.       the program segment pointer.
  294.   */
  295.  
  296.   pgm_seg = _showcs() - 16;
  297.   printf("Adjust Memory Disk v3.1 ");
  298. #if em_support
  299.   printf("(with EM Support) ");
  300. #endif
  301.   printf(" (c) Copyright 1986 by Gary Cramblitt\n");
  302.   err_code = dose_noerr;  /* set no error */
  303.  
  304.   /* ---- Make sure MS-DOS 2 or above ---- */
  305.  
  306.   _rax = dosf_getver << 8;
  307.   _doint(dosi_dosf);
  308.   if (_rax & 0x00ff < 2) {
  309.     printf ("Error -- this program requires DOS version 2 or above.\n");
  310.     exit(dose_invfunc);
  311.   }
  312.  
  313.   /*
  314.      ---- Release our own environment block.  It doesn't hurt to do so.
  315.       Done so we don't have to do it in the future when user requests
  316.       memory disk shrinkage.
  317.   */
  318.  
  319.   _res = peekw(environment_segment, pgm_seg);
  320.   _rax = dosf_dealloc << 8;
  321.   _doint(dosi_dosf);  
  322.   if (_carryf == 1) {
  323.     printf("Error -- could not free environment block.  Error code %d\n",
  324.       _rax);
  325.     exit(dose_arena);
  326.   }
  327.  
  328.   /*
  329.      ---- Parse the command line drive letter.
  330.       If user makes an error, give him usage message and exit.
  331.   */
  332.  
  333.   if (argc < 2 || 
  334.     (drive_number = (toupper(*argv[1]) - 65)) < 0 || drive_number > 8) {
  335.     dsp_usage();
  336.     exit(dose_invfunc);
  337.   }
  338.  
  339.   /*
  340.      ---- Read the boot sector from the memory disk.  Verify that we are
  341.       dealing with the correct memory disk.
  342.   */
  343.  
  344.   if ((result = readsec(drive_number, 0, &boot_sec)) != 0) {
  345.     printf("Error -- could not read boot sector.  Error code %1d\n", result);
  346.     exit(dose_invdrv);
  347.   }
  348.   if (strcmp(boot_sec.ident,"AMDISK  ") != 0) {
  349.     printf("Error -- that drive is not the adjustable memory disk!\n");
  350.     exit(dose_invdrv);
  351.   }
  352.  
  353.   /* ---- Determine the drive's current size and free space. */
  354.  
  355.   mdisk_size = boot_sec.bpb_total/sec_per_K;
  356.   _rax = dosf_drvfre << 8;
  357.   _rdx = drive_number + 1;
  358.   _doint(dosi_dosf);
  359.   if (_rax == 0xffff) {
  360.     printf("Error -- Invalid drive letter or memory disk is not loaded.\n");
  361.     exit(dose_invdrv);
  362.   }
  363.   free_secs = _rbx * _rax;    /* avail clusters * sectors per cluster */
  364.   printf("Drive %c:    Total Size: %dK       Free Space: %dK\n",
  365.     drive_number + 65, mdisk_size, free_secs/sec_per_K);
  366.  
  367.   /*  ---- Obtain the default disk. ---- */
  368.  
  369.   _rax = dosf_getdisk << 8;
  370.   _doint(dosi_dosf);
  371.   default_drive = _rax & 0x00ff;
  372.   
  373.   /* ---- Get the memory disk's current directory. ---- */
  374.  
  375.   mdisk_pathname.drv = drive_number + 'A';
  376.   mdisk_pathname.colon = ':';
  377.   mdisk_pathname.slash = '\\';
  378.   _rax = dosf_cwd << 8;
  379.   _rdx = drive_number + 1;
  380.   _rds = _showds();
  381.   _rsi = mdisk_pathname.dir;
  382.   _doint(dosi_dosf);
  383.  
  384.   /*  ---- Allocate storage for the FAT and read it in.  ---- */
  385.  
  386.   fat = calloc(boot_sec.bpb_fat_size, bytes_per_sec);
  387.   first_fat_sector = boot_sec.bpb_reserved;
  388.   last_fat_sector = first_fat_sector + boot_sec.bpb_fat_size - 1;
  389.   k = 0;
  390.   for (j = first_fat_sector; j <= last_fat_sector; j++) {
  391.     readsec(drive_number, j, fat + k);
  392.     k = k + bytes_per_sec;
  393.   }
  394.  
  395.   /*
  396.      ---- Determine what user wants to do -- expand or shrink the memory
  397.       disk, and by how much.
  398.   */
  399.  
  400.   if (argc >= 3) {
  401.     if (toupper(*argv[2]) == 'F')
  402.       mdisk_chg = atoi(argv[2]+1) - free_secs/sec_per_K;
  403.     else if (toupper(*argv[2]) == 'M') {
  404.       mdisk_chg = atoi(argv[2]+1) - free_secs/sec_per_K;
  405.       if (mdisk_chg < 0) mdisk_chg = 0;
  406.     } else {
  407.       mdisk_chg = atoi(argv[2]);
  408.       if (*argv[2] == '+' || *argv[2] == '-'); else
  409.         mdisk_chg = mdisk_chg - (boot_sec.bpb_total/sec_per_K);
  410.     }
  411.   } else exit(dose_noerr);
  412.   mdisk_size = mdisk_chg + (boot_sec.bpb_total/sec_per_K);
  413.   mdisk_size = ((mdisk_size + (sec_per_blk/sec_per_K - 1))/
  414.     (sec_per_blk/sec_per_K))*(sec_per_blk/sec_per_K); /* round to 32K */
  415.   mdisk_secs = mdisk_size * sec_per_K;
  416.   mdisk_chg = mdisk_secs - boot_sec.bpb_total;
  417.   printf("          Desired Size: %dK   Desired Change: %dK\n",
  418.     mdisk_size, mdisk_chg/sec_per_K);
  419.   if (mdisk_size < min_size || mdisk_size > max_size) {
  420.     dsp_usage();
  421.     exit(dose_invfunc);
  422.   }
  423.  
  424.   /*
  425.      ---- If expanding into Expanded Memory, check to make sure that
  426.       EMM is available.  Check is made by opening the EMM device.
  427.       If open fails, EMM is not loaded.  Get page frame segment from
  428.       EMM.  Raw DOS handle open is used here to avoid bringing in
  429.       huge DeSmet IO routines.
  430.   */
  431.  
  432.   em_requested = false;
  433. #if em_support
  434.   if (argc == 4 && toupper(*(argv[3]+1)) == 'E') {
  435.     _rax = (dosf_openh << 8) + 0;    /* open a handle function */
  436.     _rds = _showds();
  437.     _rdx = &em_device_name;        /* DS:DX => "EMMXXXX0" */
  438.     _doint(dosi_dosf);
  439.     j = _rax;                /* j = returned handle */
  440.     if (_carryf == 1) {
  441.       printf("Error -- Expanded Memory Manager is not available.\n");
  442.       printf("         Error code: %d\n",_rax);
  443.       exit(dose_invfunc);
  444.     } else {
  445.       _rax = (dosf_ioctl << 8) + 7;    /* Get output status */
  446.       _rbx = j;                /* BX = handle */
  447.       _doint(dosi_dosf);
  448.       _rbx = j;                /* BX = handle */
  449.       j = _rax & 0xff;            /* j = returned device status */
  450.       _rax = dosf_closeh << 8;        /* close handle */
  451.       _doint(dosi_dosf);
  452.       if (j == 0xff) {
  453.         em_requested = true;        /* flag EM requested */
  454.         _rax = emm_get_PFseg << 8;    /* get EM page frame segment */
  455.         _doint(usri_emm);
  456.         _rax = _rax >> 8;        /* status in AH */
  457.         if (_rax == 0) em_PFseg = _rbx;
  458.         else {
  459.           printf("Error -- Expanded Memory Manager could not report page frame.\n");
  460.           printf("         Error code: %2xH\n",_rax);
  461.           exit(dose_noram);
  462.         }
  463.       } else {
  464.         printf("Error -- Expanded Memory Manager is not available.\n");
  465.         printf("         Error code: %2xH\n",j);
  466.         exit(dose_invfunc);
  467.       }
  468.     }
  469.   }
  470. #endif
  471.  
  472.   /*
  473.      ---- Expand or shrink as indicated.  If shrinking, first make
  474.       sure there is sufficient room.
  475.   */
  476.  
  477.   if (mdisk_chg < 0 ) {
  478.     if (free_secs > -mdisk_chg) err_code = shrink();
  479.     else {
  480.       printf("Error -- NOT enough room to do the shrink.\n");
  481.       printf("         You must first erase some files.\n");
  482.       dsp_mem_blk_table();
  483.       exit(dose_noerr);
  484.     }
  485.   } else if (mdisk_chg > 0) err_code = expand();
  486.   else printf ("The disk is already that size!  Nothing to do.\n");
  487.  
  488.   /* ---- Write the new FAT back to disk */
  489.  
  490.   if (err_code == dose_noerr)
  491.     for (m = 0; m < boot_sec.bpb_fats; m++) {
  492.       k = 0;
  493.       for (j = first_fat_sector; j <= last_fat_sector; j++) {
  494.         writesec(drive_number, j, fat + k);
  495.         k = k + bytes_per_sec;
  496.       }
  497.       first_fat_sector = first_fat_sector + boot_sec.bpb_fat_size;
  498.       last_fat_sector = last_fat_sector + boot_sec.bpb_fat_size;
  499.     }
  500.  
  501.   /* ---- Write the updated boot sector back to memory disk ---- */
  502.  
  503.   if (err_code == dose_noerr)
  504.     if ((result = writesec(drive_number, 0, &boot_sec)) != 0) {
  505.       printf("Error -- could not write boot sector back.  Code: %1d\n", result);
  506.       printf("Reboot recommended.\n");
  507.       err_code = dose_invdrv;
  508.     }
  509.  
  510.   /*
  511.    ---- Restore the memory disk's default directory, then reset default
  512.     drive.
  513.   */
  514.  
  515.   if (err_code == dose_noerr) {
  516.     _rax = dosf_chdir << 8;
  517.     _rds = _showds();
  518.     _rdx = &mdisk_pathname;
  519.     _doint(dosi_dosf);
  520.  
  521.     _rax = dosf_seldisk << 8;
  522.     _rdx = default_drive;
  523.     _doint(dosi_dosf);
  524.   }
  525.  
  526.   /* ---- Free the C-allocated variables. ---- */
  527.  
  528.   free(fat);
  529.  
  530.   /* ---- Display Memory Block Table ----- */
  531.  
  532.   dsp_mem_blk_table();
  533.  
  534.   /*
  535.      ---- If expanding and EM was not requested, terminate normally by
  536.       using terminate and stay resident function.
  537.   */
  538.  
  539.   if (err_code == dose_noerr && !em_requested && mdisk_chg > 0) {
  540.     _rax = (dosf_keepprc << 8) + dose_noerr;
  541.     _rdx = sec_per_blk * par_per_sec;
  542.     _doint(dosi_dosf);            /* PROGRAM TERMINATES HERE */
  543.   }
  544.  
  545.   exit(err_code);
  546.  
  547. } /* End of main routine */
  548.  
  549. /* ==== EXPAND ROUTINE ==== */
  550. /*      Expands the memory disk by allocating new memory blocks to it. */
  551.  
  552. int expand()
  553. {
  554.   int    result, j;
  555.   int    mdisk_dif;
  556.  
  557.   /*
  558.      ---- Size the already existing main memory block to 32K.
  559.       We do this twice, once now, and once when we exit and
  560.       stay resident.  We do it now so as to detect any possible errors.
  561.       The real allocation will be done at termination time.
  562.   */
  563.  
  564.   mdisk_dif = mdisk_chg;
  565.   if (!em_requested) {
  566.     printf("Alocating new %dK memory block #%d\n",
  567.       sec_per_blk/sec_per_K, boot_sec.bpb_media);
  568.     _rax = dosf_setblk << 8;
  569.     _res = pgm_seg;
  570.     _rbx = sec_per_blk * par_per_sec;
  571.     _doint(dosi_dosf);
  572.     if (_carryf == 1) {
  573.       printf("Error -- could not modify memory block size.  Error code %1d\n",
  574.         _rax);
  575.       printf("Probably caused by not enough free memory.\n");
  576.       return(dose_noram);
  577.     }
  578.     mdisk_dif = mdisk_dif - sec_per_blk;
  579.  
  580.     /*
  581.        Increment the media byte to show an additional memory block has been
  582.        allocated.  Set the starting paragraph and size (in sectors) for the
  583.        new block in the memory block table.
  584.     */
  585.  
  586.     boot_sec.mem_blk_table[boot_sec.bpb_media].typ = nor_flg;
  587.     boot_sec.mem_blk_table[boot_sec.bpb_media].par = pgm_seg;
  588.     boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
  589.     boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = 0;
  590.  
  591.     boot_sec.bpb_media = boot_sec.bpb_media + 1;
  592.     fatput(boot_sec.bpb_media, 0, fat);  
  593.  
  594.     /* Increase the total number of sectors. */
  595.  
  596.     boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
  597.   }
  598.  
  599.   /* Now allocate new memory blocks for the remaining desired space. */
  600.  
  601.   for (;mdisk_dif > 0; mdisk_dif = mdisk_dif - sec_per_blk) {
  602.     printf("Allocating new %dK memory block #%d\n",
  603.       sec_per_blk/sec_per_K,boot_sec.bpb_media);
  604. #if em_support
  605.     if (em_requested) {
  606.       _rax = emm_get_handle << 8;
  607.       _rbx = em_pag_per_blk;
  608.       _doint(usri_emm);
  609.       _rax = _rax >> 8;
  610.       if (_rax == 0) {
  611.         boot_sec.mem_blk_table[boot_sec.bpb_media].typ = em_flg;
  612.         boot_sec.mem_blk_table[boot_sec.bpb_media].par = em_PFseg;
  613.         boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
  614.         boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = _rdx;
  615.         boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
  616.         boot_sec.bpb_media = boot_sec.bpb_media + 1;
  617.         fatput(boot_sec.bpb_media, 0, fat);
  618.       } else {
  619.         printf("Error -- could not allocate new memory block from EMM.");
  620.     printf("  Error code %2x\n",_rax);
  621.         printf("Probably caused by not enough free memory.\n");
  622.         break;
  623.       }
  624.     } else
  625. #endif
  626.     {
  627.       _rax = dosf_alloc << 8;
  628.       _rbx = sec_per_blk * par_per_sec;
  629.       _doint(dosi_dosf);
  630.       if (_carryf == 1) {
  631.         printf("Error -- could not allocate new memory block.  Error code %1d\n",
  632.           _rax);
  633.         printf("Probably caused by not enough free memory.\n");
  634.         break;
  635.       } else {
  636.         boot_sec.mem_blk_table[boot_sec.bpb_media].typ = nor_flg;
  637.         boot_sec.mem_blk_table[boot_sec.bpb_media].par = _rax;
  638.         boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
  639.         boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = 0;
  640.         boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
  641.         boot_sec.bpb_media = boot_sec.bpb_media + 1;
  642.         fatput(boot_sec.bpb_media, 0, fat);
  643.       }
  644.     }
  645.   }
  646.   return(dose_noerr);
  647.  
  648. } /* end of expand routine */
  649.  
  650. /*
  651.    ==== SHRINK ROUTINE ====
  652.     Shrinks the memory disk by moving all file sectors down
  653.     to lowest possible locations, then deallocating memory
  654.     blocks.
  655. */
  656.  
  657. int shrink()
  658. {
  659.  
  660. /* ==== Data Storage ==== */
  661.  
  662. /* ---- Root Directory ---- */
  663.  
  664.   struct dir_entry root_dir[dir_per_sec];
  665.  
  666. /* ---- Other locals ---- */
  667.  
  668.   int cur_dir;                  /* ptr to current directory */
  669.   int dir_sector;              /* current directory sector */
  670.   int mdisk_dif;              /* desired change in sectors */
  671.   char tmpstr[20];
  672.   int j, result;
  673.  
  674.   mdisk_dif = -mdisk_chg;    /* make variable positive */
  675.  
  676.   result = dose_noerr;
  677.  
  678.   /* ---- Calculate the location of the directory sectors. */
  679.  
  680.   first_dir_sector = boot_sec.bpb_reserved + 
  681.     (boot_sec.bpb_fat_size * boot_sec.bpb_fats);
  682.   last_dir_sector = (boot_sec.bpb_root * sizeof(struct dir_entry) /
  683.     bytes_per_sec) + first_dir_sector - 1;
  684.  
  685.   /* ---- First data sector follows the last root directory sector. */
  686.  
  687.   first_data_sector = last_dir_sector + 1;
  688.  
  689.   /*
  690.      ---- Locate the first available cluster in the FAT. First FAT entry
  691.       is number 2, not 0, because the first two FAT entries are used
  692.       for the media descriptor and filler.
  693.   */
  694.  
  695.   for (free_cluster = 2; fatget(free_cluster, fat) != available;
  696.     free_cluster++);
  697.  
  698.   /*
  699.      ---- Loop through the root directory, packing the clusters of
  700.       each file to the lowest possible locations.
  701.   */
  702.  
  703.   for (dir_sector = first_dir_sector; dir_sector <= last_dir_sector;
  704.     dir_sector++)
  705.   {
  706.     readsec(drive_number, dir_sector, &root_dir);
  707.     for (cur_dir = 0; cur_dir < dir_per_sec; cur_dir++)
  708.       if ((result = pack_file(&(root_dir[cur_dir]), 0, fat)) != dose_noerr)
  709.         break;
  710.     writesec(drive_number, dir_sector, &root_dir);
  711.     if (result != dose_noerr) break;
  712.   }
  713.  
  714.   /*
  715.       ---- Free up memory.  Loop backwards through the memory block table,
  716.        freeing allocated memory blocks.  As we do so, decrement the
  717.        media byte (memory block counter) and total sectors.
  718.   */
  719.  
  720.   j = boot_sec.bpb_media - 1;
  721.   if (result == dose_noerr) while (mdisk_dif >= sec_per_blk) {
  722.     if (mdisk_dif >= boot_sec.mem_blk_table[j].siz) {
  723.       printf("Freeing memory block #%d\n",j);
  724.       if (boot_sec.mem_blk_table[j].typ == nor_flg) {
  725.         _res = boot_sec.mem_blk_table[j].par;
  726.         _rax = dosf_dealloc << 8;
  727.         _doint(dosi_dosf);
  728.         if (_carryf == 1) {
  729.           printf ("Error -- could not free allocated memory block #%d.",j);
  730.           printf ("  Error code %d\n",_rax);
  731.         }
  732.       }
  733. #if em_support
  734.       else {
  735.         _rax = emm_fre_handle << 8;
  736.     _rdx = boot_sec.mem_blk_table[j].hdl;
  737.     _doint(usri_emm);
  738.     _rax = _rax >> 8;
  739.     if (_rax != 0) {
  740.           printf ("Error -- could not free EM pages for memory block #%d.",j);
  741.           printf ("  Error code %2x\n",_rax);
  742.         }
  743.       }
  744. #endif
  745.       boot_sec.bpb_media = boot_sec.bpb_media - 1;
  746.       fatput(boot_sec.bpb_media, 0, fat);
  747.       boot_sec.bpb_total = boot_sec.bpb_total - 
  748.         boot_sec.mem_blk_table[j].siz;
  749.       mdisk_dif = mdisk_dif - boot_sec.mem_blk_table[j].siz;
  750.     }
  751.   j = j - 1;
  752.   }
  753.  
  754.   return(result);
  755.  
  756. } /* end of shrink routine */
  757.  
  758. /*
  759.    ==== PACK_FILE ROUTINE ====
  760.     Given the directory entry for a file, packs all the clusters
  761.     for that file to the lowest possible locations.  If the file
  762.     is itself a subdirectory, then this routine will be called
  763.     recursively from itself.
  764.  
  765.     Clusters are packed down by moving any cluster above
  766.     "free_cluster" down to "free_cluster".  Then the next free
  767.     cluster is located and the next cluster is examined.
  768. */
  769.  
  770. int pack_file(dir, parent_cluster, fat)
  771.   struct dir_entry *dir;
  772.   int parent_cluster;
  773.   unsigned char fat[];
  774. {
  775.   struct dir_entry a_sector[dir_per_sec];
  776.   int next_cluster;
  777.   int cur_cluster;
  778.   int new_cluster;
  779.   int result;
  780.   int cur_dir;
  781.   int result;
  782.   int cluster_start;
  783.   char *tryit;
  784.   int j;
  785.  
  786.   /* Is this directory entry erased or never used?  If so, do nothing. */
  787.  
  788.   result = dose_noerr;
  789.   if (dir->u_name.status != never_used && 
  790.     dir->u_name.status != erased) {
  791.  
  792.     /* 
  793.     If file is not 0 length or it is a subdirectory file,
  794.         we can pack it.
  795.     */
  796.  
  797.     if (dir->size > 0 || isdir(dir)) {
  798.     
  799.       /* 
  800.       Handle the first cluster number, which is stored in the
  801.           directory entry, not in the FAT.
  802.       */
  803.  
  804.       next_cluster = dir->first_cluster;
  805.       if (next_cluster > free_cluster) {
  806.         new_cluster = free_cluster;
  807.         fatput(fatget(next_cluster, fat), new_cluster, fat);
  808.         dir->first_cluster = new_cluster;
  809.         fatput(available, next_cluster, fat);
  810.         for (; fatget(free_cluster, fat) != available; free_cluster++);
  811.         result = move_cluster(drive_number, next_cluster, new_cluster,
  812.           &a_sector);
  813.       }
  814.  
  815.       /* Handle rest of clusters stored in the FAT */
  816.  
  817.       if (result == dose_noerr) for (cur_cluster = dir->first_cluster;
  818.         !islast(next_cluster = fatget(cur_cluster, fat));
  819.         cur_cluster = fatget(cur_cluster, fat)) {
  820.         if (next_cluster > free_cluster) {
  821.           new_cluster = free_cluster;
  822.           fatput(fatget(next_cluster, fat), new_cluster, fat);
  823.           fatput(new_cluster, cur_cluster, fat);
  824.           fatput(available, next_cluster, fat);
  825.           for (; fatget(free_cluster, fat) != available; free_cluster++);
  826.           result = move_cluster(drive_number, next_cluster, new_cluster,
  827.             &a_sector);
  828.           if (result != dose_noerr) break;
  829.         }
  830.       }
  831.     }
  832.  
  833.     /*
  834.         If the directory entry is for a subdirectory, then process
  835.         it, packing each of its files.  Process a cluster at a time...
  836.     */
  837.  
  838.     if (isdir(dir)) for (cur_cluster = dir->first_cluster;
  839.         !islast(cur_cluster); cur_cluster = fatget(cur_cluster, fat)) {
  840.       if (result != dose_noerr) break;
  841.       cluster_start = (cur_cluster-2) * boot_sec.sec_per_cluster +
  842.         first_data_sector;
  843.  
  844.       /*  For each sector in the cluster... */
  845.  
  846.       for (j = 0; j < boot_sec.sec_per_cluster; j++) {
  847.         result = readsec(drive_number, cluster_start + j, &a_sector);
  848.         if (result != dose_noerr) break;
  849.  
  850.         /*
  851.             For each directory entry in the sector, pack it.  The first
  852.             two entries are special files ".." and "."
  853.         */
  854.  
  855.         for (cur_dir = 0; cur_dir < dir_per_sec; cur_dir++) {
  856.           if (a_sector[cur_dir].u_name.name[0] == '.') {
  857.             if (a_sector[cur_dir].u_name.name[1] == '.')
  858.               a_sector[cur_dir].first_cluster = parent_cluster;
  859.             else a_sector[cur_dir].first_cluster = dir->first_cluster;
  860.           }
  861.           else {
  862.             /* In DeSmet C, local variables are allocated on the stack. */
  863.             /* Make sure there is sufficient stack space for the recursive */
  864.             /* call.  If not, display message, return error code, and */
  865.             /* calling routines will attempt a graceful exit. Test if */
  866.             /* stack has enough room by allocating space from it, then */
  867.             /* freeing it. */
  868.             if ((tryit = calloc(700, 1)) != 0) {
  869.               free(tryit);
  870.               result = pack_file(&(a_sector[cur_dir]), dir->first_cluster, fat);
  871.             } else {
  872.               printf("Error -- Insufficient stack space to pack ");
  873.               printf("subdirectory %s\n", a_sector[cur_dir].u_name.name);
  874.               printf("Too many subdirectories.  Shrink abandoned. ");
  875.               printf("Reboot recommended.\n");
  876.               result = dose_noram;
  877.               /* Note: Even though we can't handle the subdirectory, */
  878.               /* keep going anyway so as to maintain integrity of disk. */
  879.             }
  880.           }
  881.         }
  882.         if (result != dose_noerr) break;
  883.         writesec(drive_number, cluster_start + j, &a_sector);
  884.       }
  885.     }
  886.   }
  887.   if (result != dose_noerr)
  888.     printf("Error -- Error while packing file %s\n",
  889.       dir->u_name.name);
  890.   return(result);
  891. }
  892.  
  893. /*
  894.    ==== MOVE_CLUSTER ROUTINE ====
  895.     Moves all sectors of secified cluster. If the file whose clusters
  896.     are being moved is a subdirectory, then call "pack_file" routine
  897.     recursively.
  898. */
  899.  
  900. int move_cluster (drive, from, to, buffer)
  901.   int drive, from, to;
  902.   unsigned char *buffer;
  903. {
  904.   int result;
  905.   int tmp_result;
  906.   int j;
  907.  
  908.   from = (from-2) * boot_sec.sec_per_cluster + first_data_sector;
  909.   to = (to-2) * boot_sec.sec_per_cluster + first_data_sector;
  910.   for (j = 0; j < boot_sec.sec_per_cluster; j++) {
  911.     tmp_result = readsec(drive, from + j, buffer);
  912.     result = writesec(drive, to + j, buffer);
  913.   }
  914.   return(result + tmp_result);
  915. }
  916.  
  917. /*
  918.    ==== ISDIR ROUTINE ====
  919.     Given a directory entry, returns true if the entry is for a
  920.     subdirectory file.
  921. */
  922.  
  923. int isdir(dir)
  924.   struct dir_entry *dir;
  925. {
  926.   if (dir->attr & dir_bit) return(true); else return(false);
  927. }
  928.  
  929. /*
  930.    ==== ISLAST ROUTINE ====
  931.     Given a FAT entry, returns true if it is an EOF marker.
  932. */
  933.  
  934. int islast(cluster)
  935.   int cluster;
  936. {
  937.   int j;
  938.   int result;
  939.  
  940.   result = false;
  941.   for (j = last_low; j <= last_high; j++)
  942.     if (cluster == j) result = true;
  943.   return(result);
  944. }
  945.  
  946. /*
  947.    ==== FATGET ROUTINE ====
  948.     Given cluster number and FAT, returns the 12-bit FAT entry in
  949.     and integer word, right-adjusted.
  950. */
  951.  
  952. int fatget (cluster, fat)
  953.   int cluster;
  954.   unsigned char fat[];
  955. {
  956.   int clloc, clword;
  957.  
  958.   clloc = 3*cluster/2;
  959.   clword = fat[clloc] + (fat[clloc+1] << 8);
  960.   if (cluster & 1) return (clword >> 4); else return (clword & 0x0fff);
  961. }
  962.  
  963. /*
  964.    ==== FATPUT ROUTINE ====
  965.     Given a 12-bit FAT entry, cluster number, and the FAT, stores
  966.     the entry into the FAT.
  967. */
  968.  
  969. int fatput (wd12bits, cluster, fat)
  970.   int cluster;
  971.   int wd12bits;
  972.   unsigned char fat[];
  973. {
  974.   int clloc, clword;
  975.  
  976.   clloc = 3*cluster/2;
  977.   clword = fat[clloc] + (fat[clloc+1] << 8);
  978.   if (cluster & 1)
  979.     clword = (clword & 0x000f) | (wd12bits << 4);
  980.   else
  981.     clword = (clword & 0xf000) | wd12bits;
  982.   fat[clloc] = clword & 0x00ff;
  983.   fat[clloc+1] = clword >> 8;
  984. }
  985.  
  986. /*
  987.    ==== DSP_MEM_BLK_TABLE ROUTINE ====
  988.     Displays the Memory Block Table on the screen.
  989. */
  990.  
  991. dsp_mem_blk_table()
  992. {
  993.   int j;
  994.  
  995. #if em_support
  996.   printf("\n");
  997.   printf("              Memory Block Table\n");
  998.   printf("Block #   Paragraph    Size(sectors)    EM Handle\n");
  999.   printf("-------   ---------    -------------    ---------\n");
  1000.   for (j = 0; j < boot_sec.bpb_media; ++j) {
  1001.   printf("  %1d         %4x           %3d          ",
  1002.       j,boot_sec.mem_blk_table[j].par,boot_sec.mem_blk_table[j].siz);
  1003.     if (boot_sec.mem_blk_table[j].typ == em_flg) 
  1004.       printf("  %4x",boot_sec.mem_blk_table[j].hdl);
  1005.     else printf("  --");
  1006.     printf("\n");
  1007.   }
  1008. #else
  1009.   printf("\n");
  1010.   printf("         Memory Block Table\n");
  1011.   printf("Block #   Paragraph    Size(sectors)\n");
  1012.   printf("-------   ---------    -------------\n");
  1013.   for (j = 0; j < boot_sec.bpb_media; ++j) {
  1014.   printf("  %1d         %4x           %3d\n",
  1015.       j,boot_sec.mem_blk_table[j].par,boot_sec.mem_blk_table[j].siz);
  1016.   }
  1017. #endif
  1018.  
  1019. }
  1020.  
  1021. /*
  1022.    ==== PEEKW ROUTINE ====
  1023.     Given segment and offset, returns the word thereby addressed.
  1024.     Written because DeSmet C uses small memory model and there are
  1025.     no FAR data types.
  1026. */
  1027.  
  1028. int peekw(off, seg)
  1029.   unsigned char *off;
  1030.   int seg;
  1031. {
  1032.   return((_peek(off+1, seg) << 8) + _peek(off, seg));
  1033. }
  1034.  
  1035. /*
  1036.    ==== READSEC ====
  1037.     Given drive number, desired logical sector number, and a sector
  1038.     buffer, this routine reads the sector into the buffer.
  1039.  
  1040.   NOTE: This routine uses DeSmet's "_doint" routine to perform absolute
  1041.     disk read interrupt number 25H.  This interrupt, unlike most
  1042.     interrupts, leaves the flags register on the stack when it returns.
  1043.     In DeSmet this is OK, because the called routine restores the
  1044.     stack frame prior to RETurning.  In some C compilers, this scheme
  1045.     won't work.  For example, in AZTEC C, it is the CALLING routine's
  1046.     responsibility to restore the stack frame.  In AZTEC, then, we
  1047.     would RETurn to the wrong address and die.  So look out, if you are
  1048.     converting this program to some other version of C.
  1049. */
  1050.  
  1051. int readsec (drive, sector, buffer)
  1052.   int drive;
  1053.   int sector;
  1054.   char *buffer;
  1055. {
  1056.   _rax = drive;
  1057.   _rds = _showds();  /* ASSUMPTION: DS = SS in DeSmet C */
  1058.   _rbx = buffer;
  1059.   _rcx = 1;
  1060.   _rdx = sector;
  1061.   _doint(dosi_dsk_read);
  1062.   if (_carryf == 1) return (_rax); else return (dose_noerr);
  1063. }
  1064.  
  1065. /*
  1066.    ==== WRITESEC ROUTINE ====
  1067.     Given drive number, logical sector number, and a buffer, this
  1068.     routine writes the sector to disk.
  1069.  
  1070.     See the note under routine READSEC.
  1071. */
  1072.  
  1073. int writesec (drive, sector, buffer)
  1074.   int drive;
  1075.   int sector;
  1076.   char *buffer;
  1077. {
  1078.   _rax = drive;
  1079.   _rds = _showds();    /* ASSUMPTION: DS = SS in DeSmet C */
  1080.   _rbx = buffer;
  1081.   _rcx = 1;
  1082.   _rdx = sector;
  1083.   _doint(dosi_dsk_write);
  1084.   if (_carryf == 1) return (_rax); else return (dose_noerr);
  1085. }
  1086.  
  1087. /*
  1088.    ==== DSP_USAGE ROUTINE ====
  1089.     Displays message how to use program.
  1090. */
  1091.  
  1092. int dsp_usage()
  1093.  
  1094. {
  1095. #if em_support
  1096.  printf("Usage:\n  ADJRAM drive size option\nWhere:\n");
  1097. #else
  1098.  printf("Usage:\n  ADJRAM drive size\nWhere:\n");
  1099. #endif
  1100.  printf("  drive = memory disk drive letter, A to I\n");
  1101.  printf("  size  = size of memory disk in K, %d to %d, in following format:\n",
  1102.    min_size,max_size);
  1103.  printf("            xxx   = set memory disk size to xxx\n");
  1104.  printf("            +xxx  = increase memory disk size by xxx\n");
  1105.  printf("            -xxx  = decrease memory disk size by xxx\n");
  1106.  printf("            Fxxx  = adjust size so there is xxx free space\n");
  1107.  printf("            Mxxx  = ensure there is at least xxx of free space\n");
  1108.  printf("          Size will be rounded to the next highest %dK\n",
  1109.    sec_per_blk/sec_per_K);
  1110.  printf("          If size is omitted, current drive size is displayed.\n");
  1111. #if em_support
  1112.  printf("  option= optional /E if INTEL/LOTUS/Microsoft Expanded Memory\n");
  1113.  printf("          is to be used for any expansion\n");
  1114. #endif
  1115.  printf("Examples:\n");
  1116.  printf("  ADJRAM C: 250\n");
  1117.  printf("          will set memory disk C to 256K\n");
  1118. #if em_support
  1119.  printf("  ADJRAM i: +60 /E\n");
  1120.  printf("          will increase memory disk I by 64K, using expanded memory\n");
  1121. #else
  1122.  printf("  ADJRAM i: +60\n");
  1123.  printf("          will increase memory disk I by 64K\n");
  1124. #endif
  1125.  printf("  ADJRAM C: m64\n");
  1126.  printf("          will ensure there is at least 64K free space on drive C\n");
  1127. }
  1128.